﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using Super.Common;

namespace Super.Common
{
    using Rule = KeyValue<TimeSpan, int>;

    internal static class NullMethod
    {
        /// <summary> 替换Null引用 相当于 ?? 运算, 不为source赋值.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="sender"></param>
        /// <param name="defaultValue"></param>
        /// <returns></returns>
        public static T Null<T>(this T sender, Func<T> defaultValue)
            where T : class
        {
            return sender ?? defaultValue();
        }
    }

    /// <summary> 临时替代方案
    /// </summary>
    internal class Query
    {
        public static string IP
        {
            get
            {
                string ip;
                if (HttpContext.Current.Request.ServerVariables["HTTP_VIA"] != null && !string.IsNullOrEmpty(HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"])) //使用了代理并且有透传IP
                    ip = HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];
                else
                    ip = HttpContext.Current.Request.ServerVariables["REMOTE_ADDR"];

                return ip.Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries).Any(j => { int k; return !int.TryParse(j, out k); }) ? HttpContext.Current.Request.UserHostAddress : ip;

            }
        }
    }

    /// <summary> 防入侵 防机器人 安全类
    /// </summary>
    public class RequestSecure
    {
        /// <summary> 当前实例
        /// </summary>
        private static RequestSecure current;

        /// <summary> 规则集合
        /// </summary>
        private readonly List<Rule> rules = new List<KeyValue<TimeSpan, int>>();

        /// <summary> key 计数
        /// </summary>
        private readonly Dictionary<long, List<DateTime>> IpCount = new Dictionary<long, List<DateTime>>();

        /// <summary> 当前实例
        /// </summary>
        public static RequestSecure Current
        {
            get
            {
                return current.Null(() =>
                {
                    var rs = new RequestSecure();
                    //允许 6小时 3600次请求,   6s
                    rs.AddRules(TimeSpan.FromSeconds(60 * 60 * 6), 3600);
                    //允许 10分钟 300次请求,   2s
                    rs.AddRules(TimeSpan.FromSeconds(600), 300);
                    //允许  1分钟 60次请求,    1s
                   // rs.AddRules(TimeSpan.FromSeconds(60), 60);
                    //允许 1秒钟 20次请求,    0.05s
                    rs.AddRules(TimeSpan.FromSeconds(1), 10);
                    return current = rs;
                });
            }
            set { current = value; }
        }

        /// <summary> 增加验证规则
        /// </summary>
        /// <param name="ts">时间间隔</param>
        /// <param name="count">计数</param>
        public void AddRules(TimeSpan ts, int count)
        {
            rules.Add(new Rule(ts, count));
        }

        /// <summary> 请求
        /// </summary> 
        /// <returns></returns>
        public bool Request(long key)
        {
            //根据key获取信息 
            var dts = IpCount.TryGet(key, () => new List<DateTime>());
            dts.Add(DateTime.Now);

            //按照规则校验
            foreach (var kv in rules)
            {
                var dt = DateTime.Now - kv.Key;
                if (dts.Count(j => j > dt) > kv.Value)
                {
                    return false;
                }
            }
            return true;
        }

        /// <summary> 默认Http请求处理 正常返回True
        /// </summary>
        /// <returns></returns>
        public bool Request()
        {
            //IpToKey  
            var key = Query.IP.Split('.').Aggregate(0, (j, k) => (j << 4) + int.Parse(k));
            if (!Request(key))
            {
                HttpContext.Current.Response.Write("<H2>Bad Request Your browser sent a request that this server could not understand.</H2>");
                HttpContext.Current.Response.End();
                return false;
            }
            return true;
        }
    }
}
